using System;
using System.ComponentModel;

namespace RapidHDL 
{
	/// <summary>
	/// Summary description for NodeVector.
	/// </summary>
	public class NodeVector
	{
        public NetStructure ForcedNodeStructure = NetStructure.Unknown;

		NetStructure eNodeStructure;
		System.Collections.ArrayList oNodes;
		int iSize;
		Component oParentComponent;
		NodeFlowType eNodeFlowType;
		string sName;

		public NodeVector(Component poParentComponent, string psName, int piSize)
			: this(poParentComponent,psName,piSize,NodeFlowType.Passthrough)
		{
		}

		public NodeVector(Component poParentComponent, string psName, int piSize, NodeFlowType peNodeFlowType)
		{
			int iIndex;
			Node ndNode;

			oParentComponent = poParentComponent;
			iSize = piSize;
			eNodeFlowType = peNodeFlowType;
			sName = psName;

			oNodes = new System.Collections.ArrayList(piSize);

			for(iIndex = 0; iIndex < iSize; iIndex++)
			{
				ndNode = new Node(this,iIndex);
				oNodes.Add(ndNode);
			}
			
			poParentComponent.NodeVectors.Add(psName,this);
			eNodeStructure = NetStructure.Unknown;
		}

		public NodeFlowType NodeFlowType 
		{
			set {eNodeFlowType = value;}
			get{return eNodeFlowType;}
		}

		public string Name
		{
			get{return sName;}
		}

		public int Size
		{
			get{return iSize;}
		}
		
		public Node this[int piIndex]
		{
			get{return (Node)oNodes[piIndex];}
			set
			{
				ConnectNode(piIndex,value);
			}
		}

		public System.Collections.ArrayList Nodes
		{
			get{return oNodes;}
			set
			{
				foreach(Node oConnectNode in value)
				{
					ConnectNode(oConnectNode.Position,oConnectNode);
				}
			}
		}

		public NodeVector Connection
		{
			set
			{
				ConnectNodeVector(value);
			}
		}

		public Component ParentComponent
		{
			get{return oParentComponent;}
		}

		public void Resize()
		{
		}

		public void ConnectNodeVector(NodeVector poNodeVector)
		{
            ConnectSubset(poNodeVector, 0, poNodeVector.Nodes.Count - 1);
			/*int iIndex = 0;
			foreach(Node oNode in oNodes)
			{
				oNode.Net.Connect(poNodeVector[iIndex].Net);
				iIndex++;
			}*/
		}

        public NodeVector PadZero(Component poComponent, int iWidth)
        {
            if (Nodes.Count >= iWidth)
                return this;

            string sName = poComponent.NewNodeVectorName(this.Name + iWidth.ToString() + "_pad_0");
            NodeVector nvResult = poComponent.CreateNodeVector(sName,iWidth);

            if (iWidth < Nodes.Count)
            {
                nvResult.ConnectSubset(this, 0, iWidth - 1);
            }
            else
            {
                nvResult.ConnectSubset(this, 0, Nodes.Count - 1);
                int iZeros = iWidth - Nodes.Count;
                Constant oZeros = null;
                string sZerosName = this.Name + "_const_zeros_" + iZeros.ToString();
                if (poComponent.Components.ContainsKey(sZerosName))
                {
                    oZeros = (Constant)poComponent.Components[sZerosName];
                }
                else
                    oZeros = new Constant(poComponent,sZerosName,iZeros,0);
                nvResult.ConnectSubset(Nodes.Count,iWidth - 1,oZeros.OutputNodes, 0, iZeros - 1);
            }
            return nvResult;
        }

		public void ConnectNode(int piIndex, Node poNode)
		{
			Node oNode = (Node)oNodes[piIndex];
			oNode.Net.Connect(poNode.Net);
		}

        public string NodeVectorAsStringSubset(int piStartBit, int piEndBit, bool pbPadLeft)
        {
            string sValue = NodeVectorAsString;
            return Conversion.SelectBinarySubset(sValue, piStartBit, piEndBit, pbPadLeft);
        }

		public string NodeVectorAsString
		{
			get
			{
                int iStringSize = oNodes.Count;
                string sVector = "";
				
                for (int iNodeIndex = 0; iNodeIndex < iStringSize; iNodeIndex++)
                {
                    Node oNode = (Node)oNodes[iNodeIndex];
					sVector = oNode.NodeStateAsChar + sVector;
				}
				return sVector;
			}

			set
			{
				int iStringSize = value.Length;
                if (iStringSize > oNodes.Count)
                    iStringSize = oNodes.Count;
                for (int iNodeIndex = 0; iNodeIndex < iStringSize; iNodeIndex++)
                {
                    Node oNode = (Node)oNodes[iNodeIndex];
                    oNode.NodeStateAsChar = value[value.Length - iNodeIndex - 1];
                }
                for (int iNodeIndex = iStringSize;iNodeIndex < oNodes.Count;iNodeIndex++)
                {
                    Node oNode = (Node)oNodes[iNodeIndex];
                    oNode.NodeState = NodeState.Undefined;
				}
			}
		}

		public override string ToString()
		{
			return this.NodeVectorAsString;
		}

		public string HirarchicalName
		{
			get {return oParentComponent.HirarchicalName + "." + this.Name;}
		}

        public void ConnectSubset(int piThisStart, int piThisEnd, NodeVector pnvSource, int piSourceStart, int piSourceEnd)
        {
            int iSourceStart;
            int iSourceEnd;
            int iStart;
            int iEnd;

            iSourceStart = piSourceStart;
            iSourceEnd = piSourceEnd;
            if (iSourceStart > iSourceEnd)
            {
                iSourceStart = iSourceEnd;
                iSourceEnd = piSourceStart;
            }

            iStart = piThisStart;
            iEnd = piThisEnd;
            if (iStart > iEnd)
            {
                iStart = iEnd;
                iEnd = piThisStart;
            }
            
            if (iEnd - iStart > iSourceEnd - iSourceStart)
            {
                iEnd = iStart + (iSourceEnd - iSourceStart);
            }
            if (iEnd - iStart < iSourceEnd - iSourceStart)
            {
                iSourceEnd = iSourceStart + (iEnd - iStart);
            }

            int iSourceBit = iSourceStart;
            for (int iBit = iStart; iBit <= iEnd; iBit++)
            {
                ConnectNode(iBit, (Node)pnvSource[iSourceBit]);
                iSourceBit++;
            }
        }

        public void ConnectSubset(NodeVector pnvSource, int piSourceStart, int piSourceEnd)
        {
            ConnectSubset(0, iSize - 1, pnvSource, piSourceStart, piSourceEnd);
        }

        /*public void ParseNetStructure()
        {
            oParentComponent.ComponentVerilog.VerilogTraceLog("------  Start Parsing Net Structure -----",this);
            foreach(Node oNode in oNodes)
            {
                oNode.ParseNetStructure();
            }			
        }*/

        public NetStructure OrganizeNodeStructure()
		{
			eNodeStructure = NetStructure.Unknown;

			foreach(Node oNode in oNodes)
			{
				if (eNodeStructure == NetStructure.Unknown)
					eNodeStructure = oNode.OrganizeNetStructure();
				else
				{
					if (eNodeStructure != oNode.OrganizeNetStructure())
						eNodeStructure = NetStructure.Mixed;
				}
				oParentComponent.ComponentVerilog.VerilogTraceLog("Classified Node",oNode);
			}
			return eNodeStructure;
		}

		internal NetStructure NodeStructure
		{
			get{return eNodeStructure;}
			set{eNodeStructure = value;}
		}

		public string GetLogIdString(LogType peLogType)
		{
			string sDebug = "{NodeVector: " + HirarchicalName + " [";
			if (peLogType == LogType.VerilogTrace)
			{
				sDebug += "NodeFlowType=" + NodeFlowType.ToString();
				sDebug += ",Size=" + Size.ToString();
				sDebug += ",NodeStructure=" + eNodeStructure.ToString();
			}
			sDebug +="]}";
			return sDebug;
		}

        public void AssignNodeStates(NodeVector pnAssignFrom)
        {
            NodeVectorAsString = pnAssignFrom.NodeVectorAsString;
        }

        public void ForceNodeStatesAsString(string psValue)
        {
            // force assign Node state
            // even if this is a passthrough node vector
            //    (typically you can only assign a source or floating)
            // used by simulated signal source

            int iStringSize = psValue.Length;
            if (iStringSize > oNodes.Count)
                iStringSize = oNodes.Count;
            for (int iNodeIndex = 0; iNodeIndex < iStringSize; iNodeIndex++)
            {
                Node oNode = (Node)oNodes[iNodeIndex];
                string sValue = psValue[psValue.Length - iNodeIndex - 1].ToString().ToLower();
                switch (sValue)
                {
                    case "0":
                        oNode.Net.ForceNodeState(NodeState.Low);
                        break;
                    case "1":
                        oNode.Net.ForceNodeState(NodeState.High);
                        break;
                    case "z":
                        oNode.Net.ForceNodeState(NodeState.Tri);
                        break;
                    default:
                        oNode.Net.ForceNodeState(NodeState.Undefined);
                        break;
                }
            }
            for (int iNodeIndex = iStringSize; iNodeIndex < oNodes.Count; iNodeIndex++)
            {
                Node oNode = (Node)oNodes[iNodeIndex];
                oNode.Net.ForceNodeState(NodeState.Undefined);
            }
        }

        public bool IsNodeVectorConnected(NodeVector pnvCheck)
        {
            if (pnvCheck == this)
                return true;

            if (pnvCheck.Nodes.Count != this.Nodes.Count)
                return false;

            for (int iIdx = 0; iIdx < Nodes.Count; iIdx++)
            {
                Node oNode = (Node)pnvCheck.Nodes[iIdx];
                Node oNode2 = (Node)this.Nodes[iIdx];
                if (!oNode.Net.ContainsNode(oNode2))
                    return false;
            }
            return true;
        }

        public string SimulatedNodeVectorAsString
        {
            get
            {
                return NodeVectorAsString;
            }
            set
            {
                SimulationDestabilze();        
                NodeVectorAsString = value;
                SimulationStabilze();
            }
        }

        private void SimulationDestabilze()
        {
            foreach (Node oNode in Nodes)
            {
                oNode.Net.SimulationDestabilizeNet();
            }
        }

        private void SimulationStabilze()
        {
            foreach (Node oNode in Nodes)
            {
                oNode.Net.SimulationStabilizeNet(); 
            }
        }

        public int Width
        {
            get { return Nodes.Count; }
        }

        public NodeVector Subset(Component poParentComponent, int piStart, int piEnd)
        {
            int iWidth = piEnd - piStart;
            iWidth = System.Math.Abs(iWidth) + 1;
            string sName = Name + "_s" + piStart.ToString() + "_e" + piEnd;
            sName = poParentComponent.NewNodeVectorName(sName);
            NodeVector nvResult = new NodeVector(poParentComponent, sName, iWidth);
            ConnectSubset(piStart, piEnd,nvResult, 0,nvResult.Nodes.Count - 1);
            //Utility.SinkExtraBits(poParentComponent, nvResult);
            return nvResult;
        }

        public NodeVector this[int piStart, int piEnd]
        {
            get
            {
                return Subset(this.ParentComponent, piStart, piEnd);
            }
        }

        public NodeVector Join(NodeVector pnvB)
        {
            this.Connection = pnvB;
            return this;
        }

        public static NodeVector operator !(NodeVector pnvA)
        {
            return Operators.NOT(pnvA.ParentComponent, pnvA);
        }

        public static NodeVector operator | (NodeVector pnvA, NodeVector pnvB)
        {
            return Operators.OR(pnvA.ParentComponent,pnvA,pnvB);
        }

        public static NodeVector operator & (NodeVector pnvA, NodeVector pnvB)
        {
            return Operators.AND(pnvA.ParentComponent, pnvA, pnvB);
        }

        //public static NodeVector operator << (NodeVector pnvA, int piConstant)
        public NodeVector Join(int piConstant)
        {
           return this.Join(Operators.CONST(this.ParentComponent, piConstant,this.Width));
        }

        public NodeVector Alias(string psName)
        {
            NodeVector nvAlias = this.ParentComponent.CreateNodeVector(psName, Nodes.Count);
            nvAlias.Join(this);
            return nvAlias;
        }

    }
}
